﻿using ApprovalFlow.Interface;
using ApprovalFlow.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ApprovalFlow.Services
{
    public interface ISingletonMemoryProvider : IPersistenceProvider
    {
    }

    /// <summary>
    /// IPersistenceProvider 的内存实现，用于演示和测试
    /// </summary>
    public class MemoryPersistenceProvider : ISingletonMemoryProvider
    {
        private readonly List<WorkflowInstance> _instances = new List<WorkflowInstance>();
        private readonly List<EventSubscription> _subscriptions = new List<EventSubscription>();
        private readonly List<Event> _events = new List<Event>();

        public Task<string> CreateNewWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default)
        {
            lock (_instances)
            {
                workflow.InstanceId = Guid.NewGuid().ToString();
                foreach (var pointer in workflow.ExecutionPointers)
                {
                    pointer.InstanceId = workflow.InstanceId;
                }
                _instances.Add(workflow);
                return Task.FromResult(workflow.InstanceId);
            }
        }

        public Task<WorkflowInstance> GetWorkflowInstance(string Id, CancellationToken cancellationToken = default)
        {
            lock (_instances)
            {
                return Task.FromResult(_instances.First(x => x.InstanceId == Id));
            }
        }

        public Task PersistWorkflow(WorkflowInstance workflow, CancellationToken cancellationToken = default)
        {
            lock (_instances)
            {
                var existing = _instances.First(x => x.InstanceId == workflow.InstanceId);
                _instances.Remove(existing);
                _instances.Add(workflow);
            }
            return Task.CompletedTask;
        }

        public Task<string> CreateEventSubscription(EventSubscription subscription, CancellationToken cancellationToken = default)
        {
            lock (_subscriptions)
            {
                subscription.Id = Guid.NewGuid().ToString();
                _subscriptions.Add(subscription);
            }
            return Task.FromResult(subscription.Id);
        }

        public Task<EventSubscription> GetSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default)
        {
            var result = _subscriptions.First(x => x.EventKey == eventSubscriptionId);
            return Task.FromResult(result);
        }

        public Task<IEnumerable<EventSubscription>> GetSubscriptions(string instance_id, CancellationToken cancellationToken = default)
        {
            var result = _subscriptions.Where(x => x.InstanceId == instance_id);
            return Task.FromResult(result);
        }

        public Task ClearSubscription(string eventSubscriptionId, CancellationToken cancellationToken = default)
        {
            _subscriptions.RemoveAll(x => x.Id == eventSubscriptionId);
            return Task.CompletedTask;
        }

        public Task<string> CreateEvent(Event newEvent, CancellationToken cancellationToken = default)
        {
            lock (_events)
            {
                if (_events.Contains(newEvent))
                    throw new InvalidOperationException("已经存在同一个Event对象。");
                newEvent.Id = Guid.NewGuid().ToString();
                _events.Add(newEvent);
            }
            return Task.FromResult(newEvent.Id);
        }

        public Task<EventSubscription> GetFirstOpenSubscription(string eventName, string eventKey, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(_subscriptions.FirstOrDefault(x => x.EventName == eventName && x.EventKey == eventKey));
        }

        public Task<Event> GetEvent(string id, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(_events.FirstOrDefault(x => x.Id == id));
        }

        public Task<IEnumerable<string>> GetRunnableEvents(DateTime asAt, CancellationToken cancellationToken = default)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<string>> GetEvents(string eventName, string eventKey, DateTime asOf, CancellationToken cancellationToken = default)
        {
            throw new NotImplementedException();
        }

        public Task MarkEventProcessed(string id, CancellationToken cancellationToken = default)
        {
            _events.FirstOrDefault(x => x.Id == id).IsProcessed = true;
            return Task.CompletedTask;
        }

        public Task MarkEventUnprocessed(string id, CancellationToken cancellationToken = default)
        {
            _events.FirstOrDefault(x => x.Id == id).IsProcessed = false;
            return Task.CompletedTask;
        }

        public async Task TerminateWorkflow(string workflowId)
        {
            var instance = await this.GetWorkflowInstance(workflowId);
            if (instance.Status == WorkflowStatus.Runnable)
                instance.Status = WorkflowStatus.Terminated;
            instance.ExecutionPointers.RemoveAll(x => !x.EndTime.HasValue);
            await this.PersistWorkflow(instance);
        }

        public Task<IEnumerable<string>> GetRunnableInstances(DateTime asAt, CancellationToken cancellationToken = default)
        {
            var result = _instances.Where(x => x.Status == WorkflowStatus.Runnable).Select(x => x.InstanceId);
            return Task.FromResult(result);
        }

        public Task<IEnumerable<WorkflowInstance>> GetWorkflowInstances(IEnumerable<string> ids, CancellationToken cancellationToken = default)
        {
            var result = _instances.Where(x => ids.Contains(x.InstanceId));
            return Task.FromResult(result);
        }

        public Task DeleteWorkflow(string workflowId)
        {
            _instances.RemoveAll(x => x.InstanceId == workflowId);
            _subscriptions.RemoveAll(x => x.InstanceId == workflowId);
            return Task.CompletedTask;
        }
    }

}
